package com.lzy.wzy.utils;

import com.alibaba.fastjson.JSON;
import com.google.gson.Gson;
import com.lzy.wzy.constant.MyConstant;
import com.lzy.wzy.mapper.TemplateMapper;
import com.lzy.wzy.mapper.UserMapper;
import com.lzy.wzy.mapper.WorkMapper;
import com.lzy.wzy.model.ModifyRecordBean;
import com.lzy.wzy.netty.msg.*;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.*;

@Component
@Slf4j
public class BusinessUtils {
    @Autowired
    private TemplateMapper templateMapper;

    @Autowired
    private WorkMapper workMapper;

    @Autowired
    private WorkOrderUtils workOrderUtils;

    @Autowired
    private UserMapper userMapper;

//    @Resource
//    private PlatformTransactionManager txManager;

    private final ConcurrentHashMap<String, Map<String, Channel>> rm = new ConcurrentHashMap<>(16);

    private final ConcurrentHashMap<String, List<String>> wm = new ConcurrentHashMap<>(16);

    @Autowired
    private Gson gson;

    private final ThreadPoolExecutor executor = new ThreadPoolExecutor(10
            , 20
            , 0L
            , TimeUnit.SECONDS
            , new LinkedBlockingQueue<>(40)
            ,new  ThreadPoolExecutor.CallerRunsPolicy()
    );

    public void doExecute(Map<String, Channel> channelMap, String msg, String userId) {
        executor.submit(() -> send(channelMap, msg, userId));
    }

    public void doExecute(String roomId, String msg) {
        executor.submit(() -> delSend(roomId, msg));
    }

    public void doExecute(String roomId, String msg, String workOrderNumber, int count, int templateId) {
        executor.submit(() -> addSend(roomId, msg, workOrderNumber, count, templateId));
    }


    public void doExecute(int templateId, String id, String workOrderNumber, String userId, String roomId, String operation) {
        executor.submit(() -> {
            try {
                delData(templateId, workOrderUtils.decode(id), workOrderNumber, userId, roomId, operation);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public void doExecute(Channel channel) {
        executor.submit(() -> inActive(channel));
    }

    public void doExecute(String workOrderNumber) {
        executor.submit(() -> updateStatus(workOrderNumber));
    }

    private void updateStatus(String workOrderNumber) {
        if (MyConstant.WorkOrderStatus.RELEASE.equals(workMapper.getWorkOrderStatus(workOrderNumber))) {
            workMapper.updateWorkOrderStatusAndProductDate(LocalDate.now(), MyConstant.WorkOrderStatus.InSystem, MyConstant.WorkOrderStatus.RELEASE, workOrderNumber);
        }
    }

    public void inActive(Channel channel) {
        Set<Map.Entry<String, Map<String, Channel>>> entries = rm.entrySet();
        for (Map.Entry<String, Map<String, Channel>> entry : entries) {
            entry.getValue().entrySet().removeIf(next -> next.getValue() == channel);
        }
        log.info("离线了");
    }

    public void doExecute(int templateId, String id, String field, String workOrderNumber, String userId, String roomId, AddResponseMessage responseMsg) {
        executor.submit(() -> addData(id, templateId, workOrderNumber, userId, roomId, responseMsg));
    }

    public void doExecute(String id, String val, String field, List<String> authority, int templateId, String workOrderNumber, String userId, String roomId) {
        executor.submit(() -> {
            try {
                boolean match = Arrays.stream(MyConstant.Role.PRODUCT_G).noneMatch(authority::contains);
                saveModifyRecord(id, val, field, templateId, match, workOrderNumber, userId, roomId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

    public void doExecute(List<Map<String, String>> list, String workOrderNumber, String userId, int templateId) {
        executor.submit(() -> addRecord(list, workOrderNumber, userId, templateId));
    }

    public void doExecute(String roomId, ChannelHandlerContext channelHandlerContext, String userId, String workOrderNumber,List<String> authority) {
        executor.submit(() -> connection(roomId, channelHandlerContext, userId, workOrderNumber,authority));
    }

    private void remove(String userId) {
        for (Map.Entry<String, Map<String, Channel>> entry : rm.entrySet()) {
            entry.getValue().entrySet().removeIf(next -> next.getKey().equals(userId));
        }
    }

    private void connection(String roomId, ChannelHandlerContext channelHandlerContext, String userId, String workOrderNumber,List<String> authority) {
        if (!authority.contains(MyConstant.ApproveGroup.executorG)) {
            channelHandlerContext.writeAndFlush(new TextWebSocketFrame(gson.toJson(new ConnectionResponseMessage(MyConstant.ProductionOperation.CONNECTION,false))));
            return;
        }
        remove(userId);
        Channel channel;
        Map<String, Channel> channelMap;
        if (!rm.containsKey(roomId)) {
            channel = channelHandlerContext.channel();
            ConcurrentHashMap<String, Channel> map = new ConcurrentHashMap<>();
            assert userId != null;
            map.put(userId, channel);
            rm.put(roomId, map);
        } else {
            channelMap = rm.get(roomId);
            if (!channelMap.containsKey(userId)) {
                channel = channelHandlerContext.channel();
                channelMap.put(userId, channel);
            }
        }
        if (wm.containsKey(workOrderNumber)) {
            List<String> list = wm.get(workOrderNumber);
            if (!list.contains(roomId)) {
                list.add(roomId);
            }
        } else {
            List<String> list = new ArrayList<>();
            list.add(roomId);
            wm.put(workOrderNumber, list);
        }
        channelHandlerContext.writeAndFlush(new TextWebSocketFrame(gson.toJson(new ConnectionResponseMessage(MyConstant.ProductionOperation.CONNECTION,true))));
        log.info(userId + "连接成功!!!");
    }

    private String getSingleData(int templateId, String field, String id) {
        String tableName = templateMapper.queryTableNameByTemplateId(templateId);
        return templateMapper.getSingleData(tableName, field, id);
    }

    private void saveModifyRecord(String id, String val, String field, int templateId, boolean match, String workOrderNumber, String userId, String roomId) throws Exception {
        id = workOrderUtils.decode(id);
        String singleData = getSingleData(templateId, field, id);
        if (singleData == null) {
            return;
        }
        if (StringUtils.isBlank(val)) {
            return;
        }
        if (match
                && StringUtils.isNotBlank(val)
                && StringUtils.isNotBlank(singleData)
                && !val.equals(singleData)) {
            rm.get(roomId).get(userId).writeAndFlush(otherError("不可修改"));
        } else {
            doExecute(rm.get(roomId), gson.toJson(new ModifyResponseMessage(workOrderUtils.encode(id), val, field, MyConstant.PO.MODIFY)), userId);
            updateData(id, val, field, templateId);
            List<Map<String, String>> list = new ArrayList<>();
            Map<String, String> map = new LinkedHashMap<>();
            map.put("id", id);
            map.put(field, val);
            map.put("operating", MyConstant.WorkOrderOperation.MODIFY);
            list.add(map);
            doExecute(list, workOrderNumber, userId, templateId);
        }
    }

    private void addRecord(List<Map<String, String>> list, String workOrderNumber, String userId, int templateId) {
        String str = JSON.toJSONString(list);
        ModifyRecordBean recordBean = new ModifyRecordBean();
        recordBean.setWorkOrderNumber(workOrderNumber);
        recordBean.setModifyTime(LocalDate.now());
        recordBean.setModifier(userId);
        recordBean.setModifyContent(str);
        recordBean.setType(2);
        recordBean.setTemplateId(templateId);
        workMapper.saveModifyInfo(recordBean);
    }

    private void updateData(String id, String val, String filed, int templateId) {
        String tableName = templateMapper.queryTableNameByTemplateId(templateId);
        templateMapper.updateProductData(tableName, id, val, filed);
    }

    private void addData(String id, int templateId, String workOrderNumber, String userId, String roomId, AddResponseMessage responseMsg) {
//        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
//        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//        TransactionStatus status = txManager.getTransaction(def);
        try {
            String tableName = templateMapper.queryTableNameByTemplateId(templateId);
            LinkedList<String> allFields = templateMapper.getTemplateAllFields(templateId);
            List<String> data = new ArrayList<>();
            data.add(id);
            packData(data, allFields.size());
            data.add(workOrderNumber);
            templateMapper.saveProductData(tableName, data);
            int count = templateMapper.getMachinesCountByWorkOrderNumber(tableName, workOrderNumber);
            responseMsg.setCount(count);
            doExecute(roomId, gson.toJson(responseMsg), workOrderNumber, count, templateId);
            List<Map<String, String>> list = new ArrayList<>();
            Map<String, String> map = new LinkedHashMap<>();
            map.put("id", id);
            map.put("operating", MyConstant.WorkOrderOperation.ADD);
            list.add(map);
            doExecute(list, workOrderNumber, userId, templateId);
            //txManager.commit(status);
        } catch (Exception e) {
            Channel channel = rm.get(roomId).get(userId);
            if (channel!=null) {
                channel.writeAndFlush(otherError("添加失败"));
            }
            //txManager.rollback(status);
        }
    }

    private void packData(List<String> data, int fields) {
        for (int i = 0; i < fields; i++) {
            data.add("");
        }
    }

    private void delData(int templateId, String id, String workOrderNumber, String userId, String roomId, String operation) throws Exception {
        String tableName = templateMapper.queryTableNameByTemplateId(templateId);
        LinkedHashMap<String, String> data = templateMapper.getProductDatById(tableName, id);
        if (data == null || data.isEmpty()) {
            return;
        }
        Iterator<Map.Entry<String, String>> iterator = data.entrySet().iterator();
        String val;
        while (iterator.hasNext()) {
            val = iterator.next().getValue();
            if (StringUtils.isBlank(val)) {
                iterator.remove();
            }
        }
        data.put("operating", MyConstant.WorkOrderOperation.DELETE);
        List<Map<String, String>> list = new ArrayList<>();
        list.add(data);
        templateMapper.delProductData(tableName, id);
        int count = templateMapper.getMachinesCountByWorkOrderNumber(tableName, workOrderNumber);
        doExecute(roomId, gson.toJson(new DeleteResponseMessage(workOrderUtils.encode(id), operation, count)));
        doExecute(list, workOrderNumber, userId, templateId);
    }


    private void send(Map<String, Channel> channelMap, String msg, String userId) {
        Set<Map.Entry<String, Channel>> entries = channelMap.entrySet();
        for (Map.Entry<String, Channel> entry : entries) {
            if (!entry.getKey().equals(userId)) {
                entry.getValue().writeAndFlush(new TextWebSocketFrame(msg));
            }
        }
    }

    private void delSend(String roomId, String msg) {
        Set<Map.Entry<String, Channel>> entries = rm.get(roomId).entrySet();
        for (Map.Entry<String, Channel> entry : entries) {
            entry.getValue().writeAndFlush(new TextWebSocketFrame(msg));
        }
    }

    private void addSend(String roomId, String msg, String workOrderNumber, int count, int templateId) {
        Iterator<Map.Entry<String, Map<String, Channel>>> iterator;
        Map.Entry<String, Map<String, Channel>> next;
        Map<String, Channel> stringChannelMap;
        if (count == 1) {
            String tableName = templateMapper.getTableNameByNumber(workOrderNumber, templateId);
            if (tableName != null && templateMapper.getMachinesCountByWorkOrderNumber(tableName, workOrderNumber) == 0) {
                List<String> list = wm.get(workOrderNumber);
                for (String s : list) {
                    if (s.equals(roomId))
                        continue;
                    iterator = rm.entrySet().iterator();
                    while (iterator.hasNext()) {
                        next = iterator.next();
                        if (next.getKey().equals(s)) {
                            iterator.remove();
                        }
                        stringChannelMap = rm.get(roomId);
                        stringChannelMap.putAll(next.getValue());
                    }
                }
            }
        }
        Set<Map.Entry<String, Channel>> entries = rm.get(roomId).entrySet();
        for (Map.Entry<String, Channel> entry : entries) {
            entry.getValue().writeAndFlush(new TextWebSocketFrame(msg));
        }
    }

    public TextWebSocketFrame otherError(String msg){
        return new TextWebSocketFrame(gson.toJson(new ErrorResponseMessage(MyConstant.PO.OTHER_ERROR,msg)));
    }
}
